# -*- coding: utf-8 -*-
"""
Created on Thu Aug 30 11:46:44 2012

@author: Tomek
"""

import CPPparse

glTab=""
getClassData=None
def setTab(x):
    global glTab
    glTab=x

def getTab():
    global glTab
    return glTab

def prt(s):
    print glTab+s
def it():
    global glTab
    if len(glTab)>0:
        glTab=glTab[1:]
def dt():
    global glTab
    glTab=glTab+"\t"

def checkLuaType(st):
    if len(st)<4:
        return False
    if st[len(st)-7:]=="_LUAREF" or st[len(st)-7:]=="_LUAPTR" or st[len(st)-4:]=="_LUA":
        return True
    else:
        return False
        
def getTranslatedType(nm,typ,ftype):
    if checkLuaType(typ):
        return ftype+"("+nm+")"
    else:
        return nm


def writeClassDataH(c,nam):
    for x in c["constructors"]:
        parameters=""
        for p in x["parameters"]:
            if parameters!="":
                parameters=parameters+","
            parameters=parameters+p["type"]["fullname"]+" "+p["name"]
        if len(x["parameters"])==1:
            if x["parameters"][0]["type"]["templatename"]==nam:
                parameters="const "+nam+"& "+x["parameters"][0]["name"]
        print ""
        prt(nam+"("+parameters+");")
    for x in c["fields"]:
        print ""
        prt("virtual "+x["type"]["fullname"]+" "+x["name"]+"_PARAMGET();")
        print ""
        prt("virtual void "+x["name"]+"_PARAMSET("+x["type"]["fullname"]+" setPar);")
    for x in c["methods"]:
        if x["isOperator"] and (x["operator"]=="+" or x["operator"]=="-" or x["operator"]=="*" or x["operator"]=="/" or x["operator"]=="==" or x["operator"]=="<" or x["operator"]=="<=")==False:
            continue
        parameters=""
        for p in x["parameters"]:
            if parameters!="":
                parameters=parameters+","
            if x["isOperator"]:
                parameters=parameters+p["type"]["templatename"]+" "+p["name"]
            else:
                parameters=parameters+p["type"]["fullname"]+" "+p["name"]
        print ""
        
        if x["isOperator"]:
            prt("virtual "+x["returnType"]["templatename"]+" "+x["name"]+"("+parameters+");")
        else:
            prt("virtual "+x["returnType"]["fullname"]+" "+x["name"]+"("+parameters+");")
    for x in c["classes"]:
        ctype=getClassData(x["name"])
    
        if ctype!=None and ctype.normal:
            writeClassH(c,"_LUA",False)
        if ctype!=None and ctype.reference:
            writeClassH(c,"_LUAREF",False)
        if ctype!=None and ctype.pointer:
            writeClassH(c,"_LUAPTR",False)
        
    
    
        
def writeClassDataCPP(c,acType,nam,rnam,cls):
    nameWithTemplate=""
    nameWithTemplateNew=""
    nameWithTemplateNewGet=""
    fullTemplate=""
    fullTemplateGet=""
    first=True
    for x in cls["templates"]:
        if first:
            nameWithTemplate=nameWithTemplate+"<"
            fullTemplate=fullTemplate+"template<"
            nameWithTemplateNew=nameWithTemplateNew+"<"
            first=False
        else:
            nameWithTemplate=nameWithTemplate+","
            fullTemplate=fullTemplate+","
            nameWithTemplateNewGet=nameWithTemplateNewGet+","
            fullTemplateGet=fullTemplateGet+","
        fullTemplate=fullTemplate+"typename "+x
        
        fullTemplateGet=fullTemplateGet+"typename "+x+"_GET"
        nameWithTemplate=nameWithTemplate+x
        nameWithTemplateNew=nameWithTemplateNew+x
        
        nameWithTemplateNewGet=nameWithTemplateNewGet+x+"_GET"
    if first==False:
        fullTemplate=fullTemplate+","+fullTemplateGet+">"
        nameWithTemplateNew=nameWithTemplateNew+","+nameWithTemplateNewGet+">"
        nameWithTemplate=nameWithTemplate+">"
    rnam=rnam+nameWithTemplate
    nam=nam+nameWithTemplateNew
    
    for x in c["constructors"]:
        parameters=""
        parametersOnlyName=""
        for p in x["parameters"]:
            if parameters!="":
                parameters=parameters+","
                parametersOnlyName=parametersOnlyName+","
                
            parameters=parameters+p["type"]["fullname"]+" "+p["name"]
            if checkLuaType(p["type"]["name"]):
                cacType="."
                if p["type"]["extra"]=="*":
                    cacType="->"
                parametersOnlyName=parametersOnlyName+p["name"]+cacType+"_LUA_STORE_DATA_"
            elif p["type"]["isTemplate"]:
                parametersOnlyName=parametersOnlyName+p["type"]["name"]+"_GET::getOld("+p["name"]+")"
            else:
                parametersOnlyName=parametersOnlyName+p["name"]
        print ""
        prt(fullTemplate)
        if len(x["parameters"])==1:
            if x["parameters"][0]["type"]["templatename"]==nam:
                parameters="const "+nam+"& "+x["parameters"][0]["name"]
        newCtor=rnam
        if acType=="->":
            newCtor="new "+rnam
        prt(nam+"::"+nam+"("+parameters+") : _LUA_STORE_DATA_("+newCtor+"("+parametersOnlyName+"))")
        prt("{}")
    for x in c["fields"]:
        print ""
        prt(fullTemplate)
        prt(x["type"]["fullname"]+" "+nam+"::"+x["name"]+"_PARAMGET()")
        prt("{")
        dt()
        if x["type"]["isTemplate"]:
            prt("return "+x["type"]["name"]+"_GET::getNew(_LUA_STORE_DATA_"+acType+x["name"]+");")
        else:
            prt("return "+getTranslatedType("_LUA_STORE_DATA_"+acType+x["name"],x["type"]["name"],x["type"]["templatename"])+";")
        it()
        prt("}")
        print ""
        prt(fullTemplate)
        prt("void "+nam+"::"+x["name"]+"_PARAMSET("+x["type"]["fullname"]+" setPar)")
        prt("{")
        dt()
        if x["type"]["isTemplate"]:
            prt("_LUA_STORE_DATA_"+acType+x["name"]+"="+x["type"]["name"]+"_GET::getOld(setPar);")
        else:
            prt("_LUA_STORE_DATA_"+acType+x["name"]+"=setPar;")
        it()
        prt("}")
    for x in c["methods"]:
        if x["isOperator"] and (x["operator"]=="+" or x["operator"]=="-" or x["operator"]=="*" or x["operator"]=="/" or x["operator"]=="==" or x["operator"]=="<" or x["operator"]=="<=")==False:
            continue
        parameters=""
        parametersOnlyName=""
        for p in x["parameters"]:
            if parameters!="":
                parameters=parameters+","
                parametersOnlyName=parametersOnlyName+","
                
            if x["isOperator"]:
                parameters=parameters+p["type"]["templatename"]+" "+p["name"]
            else:
                parameters=parameters+p["type"]["fullname"]+" "+p["name"]
            if checkLuaType(p["type"]["name"]):
                cacType="."
                if p["type"]["extra"]=="*":
                    cacType="->"
                parametersOnlyName=parametersOnlyName+p["name"]+cacType+"_LUA_STORE_DATA_"
            elif p["type"]["isTemplate"]:
                parametersOnlyName=parametersOnlyName+p["type"]["name"]+"_GET::getOld("+p["name"]+")"
            else:
                parametersOnlyName=parametersOnlyName+p["name"]
        print ""
        prt(fullTemplate)
        if x["isOperator"]:
            prt(x["returnType"]["templatename"]+" "+nam+"::"+x["name"]+"("+parameters+")")
        else:
            prt(x["returnType"]["fullname"]+" "+nam+"::"+x["name"]+"("+parameters+")")
        prt("{")
        dt()

        if x["returnType"]["fullname"]!="void":
            if x["isOperator"]==False:
                if x["returnType"]["isTemplate"]:
                    prt("return "+x["returnType"]["name"]+"_GET::getNew("+"_LUA_STORE_DATA_"+acType+x["name"]+"("+parametersOnlyName+")"+");")
                else:
                    prt("return "+getTranslatedType("_LUA_STORE_DATA_"+acType+x["name"]+"("+parametersOnlyName+")",x["returnType"]["name"],x["returnType"]["templatename"])+";")
            else:
                if parameters=="":
                    if x["returnType"]["isTemplate"]:
                        prt("return "+x["returnType"]["name"]+"_GET::getNew("+x["operator"]+"_LUA_STORE_DATA_"+");")
                    else:
                        prt("return "+getTranslatedType(x["operator"]+"_LUA_STORE_DATA_",x["returnType"]["name"],x["returnType"]["templatename"])+";")
                else:
                    if x["returnType"]["isTemplate"]:
                        prt("return "+x["returnType"]["name"]+"_GET::getNew(_LUA_STORE_DATA_"+x["operator"]+parametersOnlyName+");")
                    else:
                        prt("return "+getTranslatedType("_LUA_STORE_DATA_"+x["operator"]+parametersOnlyName,x["returnType"]["name"],x["returnType"]["templatename"])+";")
        else:
            prt("_LUA_STORE_DATA_"+acType+x["name"]+"("+parametersOnlyName+");")
        it()
        prt("}")
    for x in c["classes"]:
        ctype=getClassData(x["name"])
    
        if ctype!=None and ctype.normal:
            writeClassDataCPP(x["public"],".",x["name"]+"_LUA",x["name"],x)
        if ctype!=None and ctype.reference:
            writeClassDataCPP(x["public"],".",x["name"]+"_LUAREF",x["name"],x)
        if ctype!=None and ctype.pointer:
            writeClassDataCPP(x["public"],"->",x["name"]+"_LUAPTR",x["name"],x)

def writeEnumBind(e,dot=False):
    if dot:
        prt(".enum_("+e["name"]+")")
        prt("[")
        dt()
    for v in e["rows"]:
        prt("luabind::value(\""+v["name"]+"\","+str(v["value"])+")")
    if dot:
        prt("]")
        it()

def writeClassDataBind(c,cname,luaName):
    
    for x in c["constructors"]:
        parameters=""
        for p in x["parameters"]:
            if parameters!="":
                parameters=parameters+","
            parameters=parameters+p["type"]["fullname"]
        prt(".def(luabind::constructor<"+parameters+">())")
    for x in c["fields"]:
        policies=""
        if x["type"]["name"]=="std::vector" or x["type"]["name"]=="vector":
            policies=",return_stl_iterator"
        prt(".property(\""+x["name"]+"\",&"+cname+"::"+x["name"]+"_PARAMGET,&"+cname+"::"+x["name"]+"_PARAMSET"+policies+")")
    for x in c["enums"]:
        writeEnumBind(x,True)
    for x in c["methods"]:
        params=""
        operParams=""
        for p in x["parameters"]:
            if params!="":
                params=params+","
                operParams+","
            params=params+p["type"]["fullname"]
            p["type"].reloadFullname()
            if p["type"]["name"]==luaName:
                operParams=operParams+"luabind::other<"+cname+">()"
            else:
                operParams=operParams+"luabind::other<"+p["type"]["templatename"]+">()"
        if x["isOperator"]==False:
            prt(".def(\""+x["name"]+"\",("+x["returnType"]["fullname"]+"("+cname+"::*)("+params+"))&"+cname+"::"+x["name"]+")")
        else:
            withOper="luabind::self"
            if x["operator"]=="()":
                if len(x["parameters"])==1:
                    withOper=withOper+"("+operParams+")"
            else:
                if len(x["parameters"])!=1:
                    withOper=x["operator"]+withOper
                else:
                    if x["operator"]=="+" or x["operator"]=="-" or x["operator"]=="*" or x["operator"]=="/" or x["operator"]=="==" or x["operator"]=="<" or x["operator"]=="<=":
                       withOper=withOper+x["operator"]+operParams
                    else:
                        withOper=""
            if withOper!="": 
                #if x["const"]==True:
                #    withOper="luabind::const_"+withOper
                #else:
                prt(".def("+withOper+")")
    scopeWrited=False
    writeSeparator=False
    for xx in c["classes"]:
        if scopeWrited==False:
            prt(".scope")
            prt("[")
            dt()
            scopeWrited=True
        else:
            writeSeparator=True
        nameWithTemplate=""
        first=True
        for x in xx["templates"]:
            if first:
                nameWithTemplate=nameWithTemplate+"<"
                first=False
            else:
                nameWithTemplate=nameWithTemplate+","
            nameWithTemplate=nameWithTemplate+x
        if first==False:
            nameWithTemplate=nameWithTemplate+">"
        ctype=getClassData(xx["name"])
        
        if ctype!=None and ctype.normal:
            if writeSeparator:
                prt(",")
            else:
                writeSeparator=False
            bases=""
            lbases=""
            for d in xx["base"]:
                
                if bases!="":
                    bases=bases+","
                    lbases=lbases+","
                bases=bases+d[1]+" "+d[0]+"_LUA"
                lbases=lbases+d[0]+"_LUA"
            if bases!="":
                bases=" : "+bases
            if lbases!="":
                lbases="luabind::bases<"+lbases+">"
            if lbases!="":
                lbases=","+lbases
            prt("luabind::class_<"+xx["name"]+"_LUA"+lbases+">(\""+xx["name"]+"\")")
            dt()
            writeClassDataBind(xx,c["name"]+"_LUA"+nameWithTemplate,xx["name"]+"_LUA")
            it()
        if ctype!=None and ctype.reference:
            if writeSeparator:
                prt(",")
            else:
                writeSeparator=False
            bases=""
            lbases=""
            for d in xx["base"]:
                if bases!="":
                    bases=bases+","
                    lbases=lbases+","
                bases=bases+d[1]+" "+d[0]+"_LUAREF"
                lbases=lbases+d[0]+"_LUAREF"
            if bases!="":
                bases=" : "+bases
            if lbases!="":
                lbases="luabind::bases<"+lbases+">"
            if lbases!="":
                lbases=","+lbases
            prt("luabind::class_<"+xx["name"]+"_LUAREF"+lbases+">(\""+xx["name"]+"Ref\")")
            dt()
            writeClassDataBind(xx,c["name"]+"_LUAREF"+nameWithTemplate,xx["name"]+"_LUAREF")
            it()
        if ctype!=None and ctype.pointer:
            
            if writeSeparator:
                prt(",")
            else:
                writeSeparator=False
            bases=""
            lbases=""
            for d in xx["base"]:
                if bases!="":
                    bases=bases+","
                    lbases=lbases+","
                bases=bases+d[1]+" "+d[0]+"_LUAPTR"
                lbases=lbases+d[0]+"_LUAPTR"
            if bases!="":
                bases=" : "+bases
            if lbases!="":
                lbases="luabind::bases<"+lbases+">"
            if lbases!="":
                lbases=","+lbases
            prt("luabind::class_<"+xx["name"]+"_LUAPTR"+lbases+">(\""+xx["name"]+"Ptr\")")
            dt()
            writeClassDataBind(xx,c["name"]+"_LUAPTR"+nameWithTemplate,xx["name"]+"_LUAPTR")
            it()
    if scopeWrited:
        it()
        prt("]")
def writeClassH(c,nameAdd,bindStr=True):
    typ=""
    if nameAdd=="_LUAPTR":
        typ="*"
    elif nameAdd=="_LUAREF":
        typ="&"
    else:
        typ=""
    newName=c["name"]+nameAdd
    nameWithTemplate=""
    nameWithTemplateNew=""
    nameWithTemplateNewGet=""
    fullTemplate=""
    fullTemplateGet=""
    first=True
    for x in c["templates"]:
        if first:
            nameWithTemplate=nameWithTemplate+"<"
            fullTemplate=fullTemplate+"template<"
            nameWithTemplateNew=nameWithTemplateNew+"<"
            first=False
        else:
            nameWithTemplate=nameWithTemplate+","
            fullTemplate=fullTemplate+","
            nameWithTemplateNewGet=nameWithTemplateNewGet+","
            fullTemplateGet=fullTemplateGet+","
        fullTemplate=fullTemplate+"typename "+x
        
        fullTemplateGet=fullTemplateGet+"typename "+x+"_GET"
        nameWithTemplate=nameWithTemplate+x
        nameWithTemplateNew=nameWithTemplateNew+x
        
        nameWithTemplateNewGet=nameWithTemplateNewGet+x+"_GET"
    if first==False:
        fullTemplate=fullTemplate+","+fullTemplateGet+">"
        nameWithTemplateNew=nameWithTemplateNew+","+nameWithTemplateNewGet+">"
        nameWithTemplate=nameWithTemplate+">"
    oldNameWithTemplate=c["name"]+nameWithTemplate
    nameWithTemplate=newName+nameWithTemplateNew
    namespaces=c["namespace"].split("::")
    bases=""
    lbases=""
    for d in c["base"]:
        if bases!="":
            bases=bases+","
            lbases=lbases+","
        bases=bases+d[1]+" "+d[0]+nameAdd
        lbases=lbases+d[0]+nameAdd
        prt("class "+d[0]+nameAdd)
    if bases!="":
        bases=" : "+bases
    if lbases!="":
        lbases="luabind::bases<"+lbases+">"
    if lbases!="":
        lbases=","+lbases
    prt("//////////////////////////////////")
    prt("//"+c["name"]+" class generated with DarkLua")
    prt("//Please do not edit this code")
    prt("//////////////////////////////////")
    prt(fullTemplate)

    prt("class "+newName+bases)
    prt("{")
    prt("public:")
    dt()
    prt(oldNameWithTemplate+" "+typ+"_LUA_STORE_DATA_;")
    prt(newName+"("+oldNameWithTemplate+" _store_data_) : _LUA_STORE_DATA_(_store_data_)")
    prt("{}")
    prt("static void _LUAbindClass(lua_State *state,char *strAs)")
    prt("{")
    dt()
    if bindStr:
        prt("luabind::module(state)")
        prt("[")
        dt()
        for x in namespaces:
            if x!="":
                prt("luabind::namespace_(\""+x+"\")")
                prt("[")
                dt()
        prt("luabind::class_<"+newName+lbases+">(strAs)")
        dt()
        writeClassDataBind(c["public"],nameWithTemplate,newName)
        it()
        for x in namespaces:
            if x!="":
                it()
                prt("]")
            it()
        prt("];")        
        bases=""
        lbases=""
        for d in c["base"]:
            if bases!="":
                bases=bases+","
                lbases=lbases+","
            bases=bases+d[1]+" "+d[0]+nameAdd
            lbases=lbases+d[0]+nameAdd
        if bases!="":
            bases=" : "+bases
        if lbases!="":
            lbases="luabind::bases<"+lbases+">"
        if lbases!="":
            lbases=","+lbases
        it()
        prt("}")
    writeClassDataH(c["public"],nameWithTemplate)
    it()
    #prt("private:")
    #dt()
    #writeClassDataH(c["private"],nameWithTemplate)
    #it()
    #prt("protected:")
    #dt()
    #writeClassDataH(c["protected"],nameWithTemplate)
    #it()
    prt("};")